package com.home.fansea.aspact;

import com.home.fansea.annotation.RedisLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Projectname: MiniXYIot
 * @Filename: RedisLockAspect
 * @Author: FANSEA
 * @Data:2024/5/25 16:43
 */
@Aspect
@Component
public class RedisLockAspect {
    @Autowired
    RedissonClient redissonClient;


    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    private static final ExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint,RedisLock redisLock) throws Throwable {
        System.out.println("redisLock.keyName() = " + redisLock.keyName());
        //1.对锁属性进行解析获取锁key
        String lockName = this.getLockName(joinPoint,redisLock);
        System.out.println("lockName = " + lockName);
        RLock lock = redissonClient.getLock(lockName);
        //2.获取锁，如果锁获取失败则抛出异常
        boolean isLocked = false;
        try {
             isLocked  = lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), TimeUnit.SECONDS);
            System.out.println("isLocked = " + isLocked);
            if (!isLocked) {
                throw new RuntimeException("获取锁失败");
            }
            //3.执行方法，并返回结果
            return joinPoint.proceed();
        } finally  {
            if (isLocked&&lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    private String getLockName(ProceedingJoinPoint joinPoint, RedisLock redisLock) {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = resolveMethod(signature, joinPoint.getTarget());
        EvaluationContext context = new MethodBasedEvaluationContext(TypedValue.NULL, method,
                joinPoint.getArgs(), parameterNameDiscoverer);
        Expression expression = parser.parseExpression(redisLock.keyName());
        return expression.getValue(context, String.class);
    }

    private Method resolveMethod(MethodSignature signature, Object target) {
        Class<?> aClass = target.getClass();
        try{
            return aClass.getMethod(signature.getName(), signature.getParameterTypes());
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("Cannot resolve target method" + signature.getName());
        }
    }

}
